package com.ibm.testbed.script.history;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.internal.resources.File;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.xml.sax.SAXException;

import com.ibm.testbed.common.util.IScriptConstants;
import com.ibm.testbed.debug.core.sdk.util.HistoryKey;
import com.ibm.testbed.script.util.Rx;

public class HistoryManager {

	private static HistoryManager manager = null;
	
	private HistoryManager(){}
	
	public static HistoryManager getInstance(){
		if(manager == null){
			manager = new HistoryManager();
		}
		return manager;
	}
	
	public History getHistory(String path,IFile ifile){
		if (!ifile.exists()) {
			StringBuffer sb = new StringBuffer();
			sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			sb.append("<TestScript xsi:noNamespaceSchemaLocation=\"TestScriptProfile.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
			sb.append("</TestScript>");

			InputStream is = new StringBufferInputStream(sb.toString());
			try {
				ifile.create(is, true, null);
			} catch (CoreException e) {
				e.printStackTrace();
			}finally{
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}
		History history = new History();
		try {
			Rx root = Rx.findRootByPath(path);
			if(root != null){
				Rx classRx = root.findChild("MainClass");
				if(classRx == null){
					classRx = root.createChild("MainClass");
				}
				Rx[] apiRxs = root.findChilds("API");
				for(int i=0;i<apiRxs.length;i++){
					Rx apiRx = apiRxs[i];
					API api = new API();
					api.setValue(apiRx.getAttribute("value"));
					api.setRoot(apiRx);
					
					Rx[] paramRxs = apiRx.findChilds("History");
					for(int j=0;j<paramRxs.length;j++){
						Rx paramRx = paramRxs[j];
						Param param = new Param();
						param.setType(paramRx.getAttribute("type"));
						param.setActive(paramRx.getAttribute("active"));
						param.setRoot(paramRx);
						
						Rx[] valueRxs = paramRx.findChilds("Signature");
						Rx[] resultRxs = paramRx.findChilds("Result");
						for(Rx entry : valueRxs){
							Signature signature = new Signature();
							signature.setPara_name(entry.getAttribute("para_name"));
							signature.setPara_type(entry.getAttribute("para_type"));
							signature.setValue(entry.getText());
							signature.setRoot(entry);
							
							param.addSignature(signature);
						}
						for(Rx entry : resultRxs){
							Result result = new Result();
							result.setDate(entry.getAttribute("date"));
							result.setType(entry.getAttribute("type"));
							result.setValue(entry.getText());
							
							result.setRoot(entry);
							param.addResult(result);
						}
						
						api.addParam(param);
					}
					
					history.addApi(api);
				}
				
				history.setMainClass(classRx.getText());
				history.setRoot(root);
				history.setFile(ifile);
			}
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return history;
		
	}
	public History getHistory(File file){
		String filePath = file.getLocation().toString();
		String projectPath = file.getProjectRelativePath().toString();
		String temp = projectPath.substring(projectPath.indexOf("/")+1, projectPath.length());
		String path = filePath.substring(0, filePath.length()-temp.length())+IScriptConstants.historyFileName;
		IFile ifile = ResourcesPlugin.getPlugin().getWorkspace().getRoot().getFile(Path.fromPortableString(path.substring(file.getProject().getWorkspace().getRoot().getRawLocation().toString().length())));
		return this.getHistory(path,ifile);
	}
	
	public String findResult(History history,HistoryKey key){
		String apiValue = key.getTypeName()+"."+key.getFunctionName();
		Object[] values = key.getParameterValues();
		List<Signature> signatures = new ArrayList<Signature>();
		for(int i=0;i<values.length;i++){
			Signature signature = new Signature();
			signature.setValue(values[i].toString());
			signature.setPara_name(key.getParameters()[i]);
			signature.setPara_type(key.getParameterTypes()[i]);
			
			signatures.add(signature);
		}
		
		List<API> apis = history.getApis();
		for(API entry : apis){
			if(entry.getValue().equals(apiValue)){
				return this.getResult(entry, signatures);
			}
		}
		return null;
	}
	
	public Result getResult(List<Result> results){
		Result result = null;
		for(Result entry : results){
			if(result == null){
				result = entry;
			}else{
				try{
					if(Long.parseLong(entry.getDate()) > Long.parseLong(result.getDate())){
						result = entry;
					}
				}catch(NumberFormatException e){
					e.printStackTrace();
				}
			}
		}
		return result;
	}
	
	public String getResult(API api,List<Signature> values){
		List<Param> params = api.getParams();
		for(Param entry : params){
			if(isSignaturesEqual(entry.getSignatures(),values)){
				return entry.getActive();
			}
		}
		return null;
	}
	
	public void addParam(final History history,API api){
		API api1 = this.addAPI(history, api);
		Param param = this.addParam(api1, api.getParams().get(0));
		this.addResult(param, this.getResult(api.getParams().get(0).getResults()));
		
		DebugPlugin.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				ByteArrayInputStream stream = null;
				try{
					stream = new ByteArrayInputStream(history.getRoot().toString().getBytes());
					history.getFile().setContents(stream, true, true, null);
				} catch (CoreException e) {
					e.printStackTrace();
				}finally{
					if(stream != null){
						try {
							stream.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}
		});
	}
	
	public void addResult(Param param,Result result){
		for(Result entry : param.getResults()){
			if(entry.getValue().equals(result.getValue())){
				entry.getRoot().setAttribute("date", System.currentTimeMillis());
				return;
			}
		}
		
		Rx root = param.getRoot().createChild("Result");
		root.setAttribute("date", System.currentTimeMillis());
		root.setAttribute("type", result.getType());
		root.setText(result.getValue());
	}
	
	public Param addParam(API api, Param param){
		List<Param> params = api.getParams();
		List<Signature> values = param.getSignatures();
		for(Param entry : params){
			if(isSignaturesEqual(entry.getSignatures(),values)){
				return entry;
			}
		}
		
		Rx root = api.getRoot().createChild("History");
		Param result = new Param();
		result.setRoot(root);
		List<Signature> inputs = param.getSignatures();
		for(Signature entry : inputs){
			Rx child = root.createChild("Signature");
			child.setAttribute("para_name", entry.getPara_name());
			child.setAttribute("para_type", entry.getPara_type());
			child.setText(entry.getValue());
		}
		
		return result;
	}
	
	public API addAPI(History history,API api){
		List<API> apis = history.getApis();
		for(API entry : apis){
			if(entry.getValue().equals(api.getValue())){
				return entry;
			}
		}
		
		Rx root = history.getRoot().createChild("API");
		root.setAttribute("value", api.getValue());
		API result = new API();
		result.setRoot(root);
		result.setValue(api.getValue());
		
		return result;
	}
	
	public boolean isSignaturesEqual(List<Signature> list1,List<Signature> values){
		if(list1.size() == values.size()){
			for(int i=0;i<list1.size();i++){
				Signature s1 = list1.get(i);
				Signature s2 = values.get(i);
				if(!s1.getValue().equals(s2.getValue()) || !s1.getPara_name().equals(s2.getPara_name()) || !s1.getPara_type().equals(s2.getPara_type())){
					return false;
				}
			}
			return true;
		}
		return false;
	}
	
	public boolean isEqual(List<String> list1,List<String> list2){
		if(list1.size() == list2.size()){
			for(int i=0;i<list2.size();i++){
				if(!list1.get(i).equals(list2.get(i))){
					return false;
				}
			}
			return true;
		}
		return false;
	}
	public static String setParameters(Param param){
		List<Signature> values = param.getSignatures();
		StringBuilder result = new StringBuilder();
		if(values.size() > 0){
			result.append("(");
			for(int i=0;i<values.size();i++){
				if(i > 0){
					result.append(",");
				}
				if(values.get(i).getPara_type().indexOf("java.lang.String") != -1){
					result.append("\"");
				}
				result.append(values.get(i).getValue());
				if(values.get(i).getPara_type().indexOf("java.lang.String") != -1){
					result.append("\"");
				}
			}
			result.append(")");
		}
		
		return result.toString();
	}
}
